package org.rsbot.loader;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import javax.swing.JOptionPane;
import org.rsbot.loader.asm.ClassReader;
import org.rsbot.loader.script.ModScript;
import org.rsbot.loader.script.ParseException;
import org.rsbot.util.GlobalConfiguration.Paths;

public class ClientLoader
{
  private final Logger log = Logger.getLogger(ClientLoader.class.getName());
  private ModScript script;
  private Map<String, byte[]> classes;
  private int world = nextWorld();

  public void init(URL paramURL, File paramFile) throws IOException, ParseException {
    byte[] arrayOfByte;
    try { arrayOfByte = download(paramURL);

      if ((paramFile.exists()) || (paramFile.createNewFile())) {
        FileOutputStream localFileOutputStream = new FileOutputStream(paramFile);
        localFileOutputStream.write(arrayOfByte);
        localFileOutputStream.close();
      }
    } catch (IOException localIOException) {
      if (paramFile.exists()) {
        long l = paramFile.length();

        if (l == 0L) {
          throw new IOException("Unable to download ModScript data. Local copy empty.");
        }
        if (l > 2147483647L) {
          throw new IOException("Unable to download ModScript data. Local copy invalid.");
        }

        FileInputStream localFileInputStream = new FileInputStream(paramFile);
        arrayOfByte = new byte[(int)l];
        int i = 0;
        while (i < arrayOfByte.length) {
          int j = localFileInputStream.read(arrayOfByte, i, arrayOfByte.length - i);
          if (j < 0) {
            break;
          }
          i += j;
        }

        if (i < arrayOfByte.length) {
          throw new IOException("Unable to download ModScript data. Local copy could not be read fully.");
        }

        localFileInputStream.close();
      } else {
        throw new IOException("Unable to download ModScript data.");
      }
    }

    this.script = new ModScript(unpack(arrayOfByte));
  }

  public void load(File paramFile1, File paramFile2) throws IOException {
    this.classes = new HashMap();
    int i = this.script.getVersion();
    String str = this.script.getAttribute("target");

    int j = 0;
    Object localObject1;
    if ((paramFile1.exists()) && (paramFile2.exists())) {
      localObject1 = new BufferedReader(new FileReader(paramFile2));
      j = Integer.parseInt(((BufferedReader)localObject1).readLine());
      ((BufferedReader)localObject1).close();
    }
    Object localObject2;
    Object localObject3;
    Object localObject4;
    Object localObject7;
    if (i <= j) {
      localObject1 = new JarFile(paramFile1);

      checkVersion(((JarFile)localObject1).getInputStream(((JarFile)localObject1).getJarEntry("client.class")));

      this.log.info("Processing client");
      localObject2 = ((JarFile)localObject1).entries();
      while (((Enumeration)localObject2).hasMoreElements()) {
        localObject3 = (JarEntry)((Enumeration)localObject2).nextElement();
        localObject4 = ((JarEntry)localObject3).getName();
        if (((String)localObject4).endsWith(".class")) {
          localObject4 = ((String)localObject4).substring(0, ((String)localObject4).length() - 6).replace('/', '.');
          this.classes.put(localObject4, this.script.process((String)localObject4, ((JarFile)localObject1).getInputStream((ZipEntry)localObject3)));
        }
      }
    }
    else {
      this.log.info("Downloading client: " + str);
      localObject1 = getJar(str, true);
      localObject2 = getJar(str, false);

      localObject3 = Arrays.asList(this.script.getAttribute("replace").split(" "));

      localObject4 = ((JarFile)localObject2).entries();
      while (((Enumeration)localObject4).hasMoreElements()) {
        localObject5 = (JarEntry)((Enumeration)localObject4).nextElement();
        localObject6 = ((JarEntry)localObject5).getName();
        if (((String)localObject6).endsWith(".class")) {
          localObject6 = ((String)localObject6).substring(0, ((String)localObject6).length() - 6).replace('/', '.');
          this.classes.put(localObject6, load(((JarFile)localObject2).getInputStream((ZipEntry)localObject5)));
        }
      }

      localObject4 = ((JarFile)localObject1).entries();
      while (((Enumeration)localObject4).hasMoreElements()) {
        localObject5 = (JarEntry)((Enumeration)localObject4).nextElement();
        localObject6 = ((JarEntry)localObject5).getName();
        if (((String)localObject6).endsWith(".class")) {
          localObject6 = ((String)localObject6).substring(0, ((String)localObject6).length() - 6).replace('/', '.');
          if (((List)localObject3).contains(localObject6)) {
            this.log.info("Name: " + (String)localObject6);
            this.classes.put(localObject6, load(((JarFile)localObject1).getInputStream((ZipEntry)localObject5)));
          }
        }
      }

      Object localObject5 = new FileOutputStream(paramFile1);
      Object localObject6 = new JarOutputStream((OutputStream)localObject5);

      for (Iterator localIterator = this.classes.entrySet().iterator(); localIterator.hasNext(); ) { localObject7 = (Map.Entry)localIterator.next();
        ((JarOutputStream)localObject6).putNextEntry(new JarEntry((String)((Map.Entry)localObject7).getKey() + ".class"));
        ((JarOutputStream)localObject6).write((byte[])((Map.Entry)localObject7).getValue());
      }

      ((JarOutputStream)localObject6).close();
      ((FileOutputStream)localObject5).close();

      int k = 0;
      try
      {
        k = checkVersion(new ByteArrayInputStream((byte[])this.classes.get("client")));

        if (k != 0) {
          localObject7 = new FileWriter(GlobalConfiguration.Paths.getVersionCache());
          ((FileWriter)localObject7).write(Integer.toString(k));
          ((FileWriter)localObject7).close();
        }
      }
      finally
      {
        if (k != 0) {
          FileWriter localFileWriter = new FileWriter(GlobalConfiguration.Paths.getVersionCache());
          localFileWriter.write(Integer.toString(k));
          localFileWriter.close();
        }
      }

      this.log.info("Processing client");
      for (localObject7 = this.classes.entrySet().iterator(); ((Iterator)localObject7).hasNext(); ) { Map.Entry localEntry = (Map.Entry)((Iterator)localObject7).next();
        localEntry.setValue(this.script.process((String)localEntry.getKey(), (byte[])localEntry.getValue()));
      }
    }
  }

  public Map<String, byte[]> getClasses()
  {
    return this.classes;
  }

  public String getTargetName() {
    return this.script.getAttribute("target");
  }

  private int checkVersion(InputStream paramInputStream) throws IOException {
    ClassReader localClassReader = new ClassReader(paramInputStream);
    VersionVisitor localVersionVisitor = new VersionVisitor();
    localClassReader.accept(localVersionVisitor, 4);
    if (localVersionVisitor.getVersion() != this.script.getVersion()) {
      JOptionPane.showMessageDialog(null, "RSBot does not yet support the latest version of this game client.\nOur developers are currently ensuring that the bot can understand any new game content.\nThis process also ensures that the bot client remains undetectable.\nThis application will update itself when opened after the update is complete.\nTry again in a few minutes or check the powerbot.org announcements for more information.\nIf this does not give you an exact time, refrain from asking as no one else will be able to.", "Outdated (" + this.script.getName() + ")", 1);

      throw new IOException("ModScript #" + this.script.getVersion() + " != #" + localVersionVisitor.getVersion());
    }
    return localVersionVisitor.getVersion();
  }

  private JarFile getJar(String paramString, boolean paramBoolean) {
    while (true)
      try {
        String str = "jar:http://world" + this.world + "." + paramString + ".com/";
        if (paramBoolean)
          str = str + "loader.jar!/";
        else {
          str = str + paramString + ".jar!/";
        }
        URL localURL = new URL(str);
        JarURLConnection localJarURLConnection = (JarURLConnection)localURL.openConnection();
        localJarURLConnection.setConnectTimeout(5000);
        return localJarURLConnection.getJarFile();
      } catch (Exception localException) {
        this.world = nextWorld();
      }
  }

  private int nextWorld()
  {
    return 1 + new Random().nextInt(169);
  }

  private byte[] unpack(byte[] paramArrayOfByte) throws IOException {
    return load(new GZIPInputStream(new ByteArrayInputStream(paramArrayOfByte)));
  }

  private byte[] load(InputStream paramInputStream) throws IOException {
    ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream();
    byte[] arrayOfByte = new byte[4096];
    int i;
    while ((i = paramInputStream.read(arrayOfByte)) != -1) {
      localByteArrayOutputStream.write(arrayOfByte, 0, i);
    }
    return localByteArrayOutputStream.toByteArray();
  }

  private byte[] download(URL paramURL) throws IOException {
    URLConnection localURLConnection = paramURL.openConnection();
    localURLConnection.setConnectTimeout(10000);
    DataInputStream localDataInputStream = new DataInputStream(localURLConnection.getInputStream());
    byte[] arrayOfByte = new byte[localURLConnection.getContentLength()];
    localDataInputStream.readFully(arrayOfByte);
    localDataInputStream.close();
    return arrayOfByte;
  }
}